from abc import abstractmethod, ABCMeta
# 导入所需的调度器类和触发器类
from apscheduler.schedulers.background import BackgroundScheduler  # 后台运行
from apscheduler.schedulers.blocking import (
    BlockingScheduler,
)  # 主进程运行，需要单独运行

from apscheduler import events
from datetime import datetime
import six
from apscheduler.jobstores.base import ConflictingIdError, JobLookupError
from apscheduler.job import Job
from apscheduler.util import maybe_ref, undefined

# 配置信息
from scheduler_package.config import LISTENER, JOB_EXECUTORS
from scheduler_package.config import (
    TIME_ZONE,
    JOB_DEFAULTS,
    JOB_STORE,
    Scheduler_Logger,
)  # 调度器配置

# 添加任务
from scheduler_package.tasks.add_task_to_scheduler import InherentAddTask


# 基类
class BaseScheduler(metaclass=ABCMeta):
    TIME_FORMAT = "%Y-%m-%d %H:%M:%S"

    def __init__(self):
        self.scheduler = self._scheduler_obj()
        self.logger = Scheduler_Logger.get("logger")

    # 1、初始化调度器
    @abstractmethod
    def _scheduler_obj(self):
        raise ValueError("必须重构此方法")

    # 2、添加任务
    def _add_job(self):
        # 添加固定的定时任务
        InherentAddTask(self.scheduler)

    # 3、添加监听器
    def _listener(self, event: events):
        code = event.code
        run_time = datetime.now().strftime(self.TIME_FORMAT)
        msg = LISTENER.get(code)
        # 存储器
        jobstore = self.scheduler._jobstores["default"]
        job_history_data = {
            "job_id": None,
            "run_time": None,
            "is_error": 0,
            "error_msg": None,
        }

        if code == 4096:
            # 成功运行
            job_id = event.job_id
            job_history_data["job_id"] = job_id
            job_history_data["run_time"] = run_time
            # 记录到数据库中
            jobstore.insert_job_history(job_history_data)
        elif code == 8192 or code == 16384:
            # 运行异常了
            job_id = event.job_id
            job_history_data["job_id"] = job_id
            job_history_data["run_time"] = run_time
            job_history_data["is_error"] = 1
            job_history_data["error_msg"] = msg
            # 记录到数据库中
            jobstore.insert_job_history(job_history_data)
        elif code in (1, 2, 4, 8, 32, 128, 1024, 2048):
            ###调度器启动时
            self.logger.info(msg)
            try:
                job_id = event.job_id
                if msg:
                    msg = "任务id={}，{}".format(job_id, msg)
                    self.logger.info(msg)
            except Exception:
                if msg:
                    self.logger.info(msg)

    # 4、启动定时器
    def start(self):
        # 1、设置定时任务(监听器会先监听到任务添加，再监听到调度器启动)
        self._add_job()
        # 2、设置监听器
        self.scheduler.add_listener(self._listener)
        # 3、启动调度器
        try:
            # print('{},定时器启动成功,等待定时任务执行...'.format(datetime.now().strftime(self.TIME_FORMAT)))
            self.scheduler.start()
            self.logger.info("启动调度器成功...")
        except KeyboardInterrupt:
            self.logger.exception("手动退出定时器")
        except Exception as e:
            self.logger.exception(f"定时器报错了，{e}")
        finally:
            self.scheduler.shutdown()


# 阻塞式进程
class RunBlockScheduler(BaseScheduler):
    def _scheduler_obj(self):
        obj = BlockingScheduler()
        obj.configure(
            timezone=TIME_ZONE,  # 时区
            job_defaults=JOB_DEFAULTS,  # job的默认配置
            jobstores=JOB_STORE,  # job的存储后端
            gconfig=Scheduler_Logger,  # 日志记录相关的的配置
            executors=JOB_EXECUTORS,  # 执行器
            max_instances=3,  # 3秒检查一次任务是否需要执行
        )
        return obj


# 后台式运行
class RunBackgroundScheduler(BaseScheduler):
    def _scheduler_obj(self):
        obj = BackgroundScheduler()
        obj.configure(
            timezone=TIME_ZONE,  # 时区
            job_defaults=JOB_DEFAULTS,  # job的默认配置
            jobstores=JOB_STORE,  # job的存储后端
            gconfig=Scheduler_Logger,  # 日志记录相关的的配置
            executors=JOB_EXECUTORS,  # 执行器
            max_instances=3,  # 3秒检查一次任务是否需要执行
        )
        return obj


# 动态添加任务使用的
class RunAddJobScheduler(BackgroundScheduler):
    def add_job(
        self,
        func,
        trigger=None,
        args=None,
        kwargs=None,
        id=None,
        name=None,
        misfire_grace_time=undefined,
        coalesce=undefined,
        max_instances=undefined,
        next_run_time=undefined,
        jobstore='default',
        executor='default',
        replace_existing=False,
        **trigger_args,
    ):

        # 动态添加的任务是否可以执行的关键（超时未执行时，允许执行一次）
        misfire_grace_time = misfire_grace_time if misfire_grace_time else 3600
        # 合并错过的任务，只执行一次
        coalesce = coalesce if coalesce else True
        job_kwargs = {
            "trigger": self._create_trigger(trigger, trigger_args),
            "executor": executor,
            "func": func,
            "args": tuple(args) if args is not None else (),
            "kwargs": dict(kwargs) if kwargs is not None else {},
            "id": id,
            "name": name,
            "misfire_grace_time": misfire_grace_time,
            "coalesce": coalesce,
            "max_instances": max_instances,
            "next_run_time": next_run_time,
        }
        job_kwargs = dict(
            (key, value)
            for key, value in six.iteritems(job_kwargs)
            if value is not undefined
        )
        # 将定时任务添加到数据库中
        job = Job(self, **job_kwargs)
        self._real_add_job(job, jobstore, replace_existing)
        return job

    def start(self, *args, **kwargs):
        raise Exception("该调度器，不能启动，只用于新增和修改任务")
